//	Copyright (c) 2008, x-port.net ltd.
//
//	All rights reserved.
//
//	Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
//
//	    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
//	    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
//	    * Neither the name of the x-port.net ltd. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
//
//	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
//	A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//   Purpose:  Protocol utility function definitions..
//   $Id$


#include <stdafx.h>
#include <Common/ProtocolUtil.h>
#include <Common/RegistryUtil.h>
#include <Common/FileSystemUtil.h>

#define FILE_PROTOCOL_STR "file://"
#define COOKIE_PROTOCOL_STR "cookie:"
#define WEBAPP_PROTOCOL_STR "webapp:"
#define COOKIES_DIRECTORY_STR "\\x-port.net\\formsPlayer\\cookies\\"
#define WEBAPPS_DIRECTORY_STR "\\Sidewinder\\webapps\\"
#define LOCALHOST_STR "localhost"
#define PARENT_DIRECTORY_STR ".."
#define DIRECTORY_SEPARATOR '/'

// Resolve a protocol-based path to its local machine equivalent.
HRESULT xport::ResolveProtocol(const char *sURL, char **psResolvedPath, const char *sCurrentURL, const bool bCreateDirectories)
{
	HRESULT hr = E_FAIL;
	// Check the arguments.
	if(sURL && psResolvedPath) {
		// Get the location of the user's Application Data directory.
		char *sAppData = 0;
		hr = xport::RegGetValueString(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", "AppData", &sAppData);
		if(SUCCEEDED(hr)) {
			if(sAppData) {
				// Call the appropriate protocol-specific resolution function.
				if(::strnicmp(sURL, COOKIE_PROTOCOL_STR, ::strlen(COOKIE_PROTOCOL_STR)) == 0)
					hr = ResolveCookiePath(sURL, sAppData, sCurrentURL, psResolvedPath, bCreateDirectories);
				else if(::strnicmp(sURL, WEBAPP_PROTOCOL_STR, ::strlen(WEBAPP_PROTOCOL_STR)) == 0)
					hr = ResolveWebappPath(sURL, sAppData, psResolvedPath, bCreateDirectories);
				else
					hr = S_FALSE;
			} else
				hr = E_OUTOFMEMORY;
		}
		if(sAppData)
			::free(sAppData);
	} else
		hr = E_INVALIDARG;
	return hr;
}

// Resolve a path that uses the webapp: protocol.
HRESULT xport::ResolveWebappPath(const char *sPath, const char *sAppData, char **psResolvedPath, const bool bCreateDirectories)
{
	HRESULT hr = E_FAIL;
	// Check the arguments.
	if(sPath && sAppData && psResolvedPath) {
		// Ensure that the URL does not attempt to get out of the sandbox.
		hr = EnsurePathDepth(sPath + ::strlen(WEBAPP_PROTOCOL_STR));
		if(SUCCEEDED(hr)) {
			// Calculate the number of initial directory separators.
			const char *pFirstSeparators = sPath + ::strlen(WEBAPP_PROTOCOL_STR);
			unsigned long nFirstSeparators = 0;
			while(*pFirstSeparators++ == DIRECTORY_SEPARATOR)
				nFirstSeparators++;

			// Allocate memory for the resolved path string.
			*psResolvedPath = reinterpret_cast<char *>(::malloc(sizeof(char) * (::strlen(FILE_PROTOCOL_STR)         //   [file://]
																				+ ::strlen(sAppData)              // + [C:\...\Application Data]
																				+ ::strlen(WEBAPPS_DIRECTORY_STR) // + [\x-port.net\formsplayer\webapps\]
																				+ ::strlen(sPath)                 // + [webapp://webapp/data.xml]
																				- ::strlen(WEBAPP_PROTOCOL_STR)   // - [webapp:]
																				- nFirstSeparators                // - [//]
																				+ 1)));                           // + ['\0']
			if(*psResolvedPath) {
				// Construct the resolved path string.
				::strcpy(*psResolvedPath, FILE_PROTOCOL_STR);
				::strcat(*psResolvedPath, sAppData);
				::strcat(*psResolvedPath, WEBAPPS_DIRECTORY_STR);
				::strcat(*psResolvedPath, sPath + ::strlen(WEBAPP_PROTOCOL_STR) + nFirstSeparators);
				hr = S_OK;
				if(bCreateDirectories) {
					// Now lop off the file portion...
					char *p2 = ::strrchr(*psResolvedPath + ::strlen(FILE_PROTOCOL_STR), '/');
					if(p2)
						*p2 = '\0';
					else {
						p2 = ::strrchr(*psResolvedPath + ::strlen(FILE_PROTOCOL_STR), '\\');
						if(p2)
							*p2 = '\0';
					}
					// ...and ensure that the resolved path exists on the local machine.
					while(char *p3 = ::strrchr(*psResolvedPath + ::strlen(FILE_PROTOCOL_STR), '/'))
						*p3 = '\\';
					CreateDirectoryStructure(*psResolvedPath + ::strlen(FILE_PROTOCOL_STR));
					// Reinstate the file portion.
					if(p2)
						*p2 = '/';
				}
			} else
				hr = E_OUTOFMEMORY;
		}
	} else
		hr = E_INVALIDARG;
	return hr;
}

// Resolve a path that uses the cookie: protocol.
HRESULT xport::ResolveCookiePath(const char *sPath, const char *sAppData, const char *sCurrentURL, char **psResolvedPath, const bool bCreateDirectories)
{
	HRESULT hr = E_FAIL;
	// Check the arguments.
	if(sPath && sAppData && sCurrentURL && psResolvedPath) {
		// Ensure that the URL does not attempt to get out of the sandbox.
		hr = EnsurePathDepth(sPath + ::strlen(COOKIE_PROTOCOL_STR));
		if(SUCCEEDED(hr)) {
			// Determine the domain for this cookie.
			bool bReplaceSlash = false;
			char *p = strchr(sCurrentURL, ':'), *sDomain = 0;
			if(p) {
				*p = '\0';
				if(stricmp(sCurrentURL, "file") != 0) {
					*p = ':';
					p++;
					if(*p != '\\') {
						while(*p == '/')
							p++;
						sDomain = p;
						// Bug 1123 - Need to remove the port number seperator (':') as this is a invalid
						// character for the file system.
						char * pDirSep = strchr(sDomain, '/') ;
						char * pPortSep = strchr(sDomain, ':') ;
						p  = (pPortSep && pPortSep < pDirSep) ? pPortSep : pDirSep;
						if(p) {
							*p = '\0';
							bReplaceSlash = true;
						}
					}
				} else
					*p = ':';
			}

			// Calculate the number of initial directory separators.
			const char *pFirstSeparators = sPath + ::strlen(COOKIE_PROTOCOL_STR);
			unsigned long nFirstSeparators = 0;
			while(*pFirstSeparators++ == DIRECTORY_SEPARATOR)
				nFirstSeparators++;

			// Allocate memory for the resolved path string.
			*psResolvedPath = reinterpret_cast<char *>(::malloc(sizeof(char) * (::strlen(FILE_PROTOCOL_STR)                     //   [file://]
																				+ ::strlen(sAppData)                          // + [C:\...\Application Data]
																				+ ::strlen(COOKIES_DIRECTORY_STR)             // + [\x-port.net\formsplayer\cookies\]
																				+ ::strlen(sDomain ? sDomain : LOCALHOST_STR) // + [e.g. www.formsplayer.com]
																				+ 1                                           // + [\]
																				+ ::strlen(sPath)                             // + [cookie://cookie.xml]
																				- ::strlen(COOKIE_PROTOCOL_STR)               // - [cookie:]
																				- nFirstSeparators                            // - [//]
																				+ 1)));                                       // + ['\0']
			if(*psResolvedPath) {
				// Construct the resolved path string.
				::strcpy(*psResolvedPath, FILE_PROTOCOL_STR);
				::strcat(*psResolvedPath, sAppData);
				::strcat(*psResolvedPath, COOKIES_DIRECTORY_STR);
				::strcat(*psResolvedPath, sDomain ? sDomain : LOCALHOST_STR);
				::strcat(*psResolvedPath, "\\");
				::strcat(*psResolvedPath, sPath + ::strlen(COOKIE_PROTOCOL_STR) + nFirstSeparators);
				hr = S_OK;
				if(bCreateDirectories) {
					// Now lop off the file portion...
					char *p2 = ::strrchr(*psResolvedPath + ::strlen(FILE_PROTOCOL_STR), '/');
					if(p2)
						*p2 = '\0';
					else {
						p2 = ::strrchr(*psResolvedPath + ::strlen(FILE_PROTOCOL_STR), '\\');
						if(p2)
							*p2 = '\0';
					}
					// ...and ensure that the resolved path exists on the local machine.
					while(char *p3 = ::strrchr(*psResolvedPath + ::strlen(FILE_PROTOCOL_STR), '/'))
						*p3 = '\\';
					CreateDirectoryStructure(*psResolvedPath + ::strlen(FILE_PROTOCOL_STR));
					// Reinstate the file portion.
					if(p2)
						*p2 = '/';
				}
			} else
				hr = E_OUTOFMEMORY;
			if(bReplaceSlash)
				*p = '/';
		}
	} else
		hr = E_INVALIDARG;
	return hr;
}

// Ensure a path does not go "higher" up the tree than its own root. Recursive.
HRESULT xport::EnsurePathDepth(const char *sPath, long nDepth)
{
	HRESULT hr = S_OK;
	// Check the arguments.
	if(sPath && nDepth >= 0) {
		// Get the first directory separator.
		char *p = ::strchr(sPath, DIRECTORY_SEPARATOR);
		if(p) {
			while(*p == DIRECTORY_SEPARATOR)
				p++;
			// Adjust the depth according to whether we have a valid directory or a parent directory symbol.
			if(::strncmp(p, PARENT_DIRECTORY_STR, ::strlen(PARENT_DIRECTORY_STR)) == 0)
				nDepth--;
			else
				nDepth ++;
			// If the depth is still valid...
			if(nDepth >= 0)
				// ...recurse.
				hr = EnsurePathDepth(p, nDepth);
			else
				hr = E_ABORT;
		} else if(nDepth <= 0)
			hr = E_ABORT;
	} else
		hr = E_INVALIDARG;
	return hr;
}

